import { format as formatFns, parseISO } from 'date-fns';

/**
 * @description 格式化控制器
 * @export
 * @class FormatController
 */
export class FormatController {
	/**
	 * @description 唯一实例
	 * @private
	 * @static
	 * @memberof FormatController
	 */
	private static readonly instance = new FormatController();

	/**
	 * Creates an instance of FormatController.
	 * @memberof FormatController
	 */
	private constructor() {
		if (FormatController.instance) {
			return FormatController.instance;
		}
	}

	/**
	 * @description 获取唯一实例
	 * @static
	 * @return {*}  {FormatController}
	 * @memberof FormatController
	 */
	public static getInstance(): FormatController {
		return FormatController.instance;
	}

	/**
	 * 格式化
	 *
	 * @param {*} value 值
	 * @param {*} formatRule 格式化规则
	 * @memberof FormatController
	 */
	public format(value: any, formatRule: any) {
		let _value = value;
		if (value && formatRule) {
			const returnValue = this.formatData(value, formatRule);
			_value = this.encodeHtml(returnValue);
		}
		return _value;
	}

	/**
	 * @description 格式化时间
	 * @private
	 * @param {*} newValue 新值
	 * @param {*} oldValue 原始值
	 * @param {string} dateFormat 日期格式化
	 * @return {*}
	 * @memberof FormatController
	 */
	private formatDate(newValue: any, oldValue: any, dateFormat: string) {
		if (isNaN(oldValue) && !isNaN(Date.parse(oldValue))) {
			if (parseISO(oldValue)) {
				const tempFormat = dateFormat.replace(/Y/g, 'y').replace(/D/g, 'd');
				return formatFns(parseISO(oldValue), tempFormat);
			} else {
				return oldValue;
			}
		} else {
			return newValue;
		}
	}

	/**
	 * @description 乘法 ,解决js小数相乘精度缺失问题
	 * @date 2023/02/15 18:02:08
	 * @private
	 * @param {number} arg1
	 * @param {number} arg2
	 * @returns {*}  
	 * @memberof FormatController
	 */
	private accMul(arg1: number, arg2: number) {
		var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
		try { m += s1.split(".")[1].length } catch (e) { }
		try { m += s2.split(".")[1].length } catch (e) { }
		return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
	}

	/**
	 * @description 格式化数据
	 * @private
	 * @param {string} value 值
	 * @param {string} formatRule 格式化规则
	 * @return {*}
	 * @memberof FormatController
	 */
	private formatData(value: any, formatRule: string) {
		const oldValue = value;
		let usedCalc = false;
		const formatRules = formatRule.split(';');
		if (
			formatRules.length < 4 &&
			formatRules[0].match(/^\[(>|<|=|>=|<=|<>)\d+\]/)
		) {
			usedCalc = true;
		}
		let newFormatRule = this.handleFormatRules(value, formatRules);
		// 遇到 % 乘以 100
		if (
			newFormatRule.match(/[^*|\\|_]%/) !== null &&
			newFormatRule.match(/"%"/) === null &&
			newFormatRule.match(/'%'/) === null &&
			value.toString().match(/^-?\d+(\.\d+)?$/)
		) {
			value = this.accMul(value,100);
		}
		let codeZhengshuNumCount = 0;
		let codeXiaoshuNumCount = 0;
		let allEffectivePlaceholder = true;
		let isQianfenwei = false;
		let isFenShuwei = false;
		// 处理自定义格式中整数部分数字占位符个数
		if (newFormatRule.match(/(([#|0|\?](\\\.|[^#|0|\.])*)+)\.?/) !== null) {
			const temp = newFormatRule
				.replace(/[*|\\|_]{2}/g, '')
				.replace(/[*|\\|_]([#|0|\?])/g, '')
				.match(/(([#|0|\?](\\\.|[^#|0|\.])*)+)\.?/);
			if (temp) {
				codeZhengshuNumCount = temp[1].match(/([#|0|\?])/g)?.length || 0;
			}
		}
		// 处理千分位
		if (newFormatRule.match(/[^*|\\|_],/) !== null) {
			isQianfenwei = true;
			const endQianfenwei = (newFormatRule + ' ').match(
				/[^*|\\|_](,+)[^#|0|\?]/
			);
			if (endQianfenwei !== null) {
				// 是否有末尾千分位
				value = value / Math.pow(1000, endQianfenwei[1].length);
			}
		}
		// 处理小数
		if (newFormatRule.match(/\.(([#|0|\?]?(\\\.|[^#|0|\.])*)+)/) !== null) {
			const xiaoshuCode = (
				newFormatRule
					.replace(/[*|\\|_]{2}/g, '')
					?.replace(/[*|\\|_]([#|0|\?])/g, '')
					?.match(/\.(([#|0|\?]?(\\\.|[^#|0|\.])*)+)/) as any
			)[1];
			const xiaoshuNumSlot = xiaoshuCode?.match(/([#|0|\?])/g);
			if (xiaoshuCode.match(/^#+%?$/) === null) {
				allEffectivePlaceholder = false;
			}
			if (xiaoshuNumSlot === null) {
				codeXiaoshuNumCount = 0;
			} else {
				codeXiaoshuNumCount = xiaoshuNumSlot.length;
			}
		} else {
			// rule没有小数部分，直接四舍五入
			if (typeof value === 'number') {
				value = Math.round(value);
			}
		}
		// 处理分数
		const tempFenshuMatch = newFormatRule
			.replace(/[*|\\|_]{2}/g, '')
			.replace(/[*|\\|_]([#|0|\?])/g, '')
			.match(/(.*[^*|\\|_])\/[^\d|#|0|\?]*?([\d|#|0|\?]+)/);
		let fenmu: any;
		if (tempFenshuMatch) {
			//如果是分数表达式
			value = oldValue;
			const isHasZhengshu: any =
				tempFenshuMatch[1].match(/([#|0\?]+[^#|0\?]*)/g);
			fenmu = tempFenshuMatch[2];
			if (tempFenshuMatch.length >= 2) {
				isFenShuwei = true;
			}
			if (fenmu === '?') {
				const temp = this.parseNumber(parseFloat(value), 1);
				fenmu = temp[1];
			} else if (fenmu === '??') {
				const temp = this.parseNumber(parseFloat(value), 2);
				fenmu = temp[1];
			} else if (fenmu === '???') {
				const temp = this.parseNumber(parseFloat(value), 3);
				fenmu = temp[1];
			}
			const runValue = parseInt(
				((parseInt((value * fenmu * 2).toString()) + 1) / 2).toString()
			);
			if (isHasZhengshu.length >= 2) {
				value = [parseInt((runValue / fenmu).toString()), runValue % fenmu];
			}
			if (isHasZhengshu.length >= 2) {
				codeZhengshuNumCount =
					isHasZhengshu[0].match(/([#|0\?])[^#|0\?]*/g).length;
				codeXiaoshuNumCount =
					isHasZhengshu[1].match(/([#|0\?])[^#|0\?]*/g).length;
			} else {
				codeZhengshuNumCount = 0;
				codeXiaoshuNumCount = isHasZhengshu[0].length;
				value = [0, runValue];
			}
		} else {
			// 如果是一个极小的数字，例如9.568181142949328e-7
			const e = value.toString().match(/e([+|-])(\d+)$/);
			value = value.toString().split('.');
			if (e) {
				if (e[1] === '-') {
					if (e[2] > 0) {
						value = [
							0,
							Array(e[2] - 1)
								.fill(0)
								.join('') +
								value[0] +
								value[1].replace(/e-(\d+)$/, ''),
						];
					}
				}
			}
		}
		value[0] = Math.abs(value[0]).toString();
		let temp = '';
		const returnValue = ['', '', ''];
		let returnHtml = '';
		// 已经跑完的数字位置
		let finishedNumCount = 0;
		let styleColor = '';
		while (newFormatRule.length > 0) {
			temp = newFormatRule[0];
			if (temp === '_') {
				returnHtml +=
					'<span style="opacity: 0">' + newFormatRule[1] + '</span>';
				newFormatRule = newFormatRule.slice(2);
			} else if (temp === '*') {
				returnValue[0] = returnHtml;
				if (newFormatRule[1] !== '=') {
					returnValue[1] += newFormatRule[1];
				}
				returnHtml = '';
				newFormatRule = newFormatRule.slice(2);
			} else if (temp === '[') {
				newFormatRule = newFormatRule.slice(1);
				const type: any = newFormatRule.match(/^([^\]]+)\]/);
				const styleColorList: any = {
					红色: 'red',
					黑色: 'black',
					黄色: 'yellow',
					绿色: 'green',
					白色: 'white',
					蓝色: 'blue',
					青色: 'cyan',
					洋红: 'magenta',
				};
				const findStr = newFormatRule.match(/^\[\$(\S)-804\]/);
				if (Object.keys(styleColorList).includes(type[1])) {
					styleColor = styleColorList[type[1]];
					returnHtml += '';
				} else if (findStr) {
					newFormatRule = newFormatRule.replace(/^\[\$(\S)-804\]/, '');
					returnHtml += findStr[1];
				}
				newFormatRule = newFormatRule.slice(type.length + 1);
			} else if (temp === '#' || temp === '0' || temp === '?') {
				const match = newFormatRule.match(/^([#|0]+)\.([#|0]+)E\+([#|0]+)/);
				if (match) {
					// 科学计数法
					let match1Length = match[1].length;
					const match2Length = match[2].length;
					const match3Length = match[3].length;
					let firstWeishu = value[0].length; //整数位数
					let chengshu = 0;
					if (value.length > 1) {
						//有小数部分
						if (value[0] === 0) {
							//数字是小于1的
							value[0] = '';
							let ppp = 0;
							while (ppp++ < 100) {
								if (value[1][0] !== '0') {
									value[0] += value[1][0];
								}
								value[1] = value[1].slice(1);
								chengshu--;
								if (value[1].length === 0) {
									//if(value[0]=='0'){
									value = [value[0]];
									firstWeishu = value[0].length;
									break;
								}
							}
							//小于1的,表达式整数无论多少位,只进步到一位有效数字,在excel尝试得到,不知道原因
							if (match1Length > 1) {
								for (let i = 0; i < match1Length - 1; i++) {
									returnHtml += '0';
								}
							}
							match1Length = 1;
						} else {
							value = [value[0] + value[1]];
						}
					}
					returnHtml += value[0].toString().slice(0, match1Length) + '.'; //整数位
					//小数部分
					let xiaoshuValue = '';
					for (
						let i = match2Length + match1Length;
						i >= match1Length + 1;
						i--
					) {
						if (
							newFormatRule[i] === '#' &&
							xiaoshuValue === '' &&
							value[0][i - 1] === '0'
						) {
							return;
						} else if (value[0][i - 1] !== undefined) {
							xiaoshuValue = value[0][i - 1] + xiaoshuValue;
						} else if (newFormatRule[i] === '0') {
							xiaoshuValue = '0' + xiaoshuValue;
						}
					}
					returnHtml += xiaoshuValue + 'E';
					returnHtml += firstWeishu > match1Length - chengshu ? '+' : '-';
					//指数部分
					const valueTemp = Math.abs(firstWeishu - match1Length + chengshu);
					if (valueTemp.toString().length < match3Length) {
						for (
							let i = 0;
							i < match3Length - valueTemp.toString().length;
							i++
						) {
							returnHtml += '0';
						}
					}
					returnHtml += valueTemp;
					newFormatRule = newFormatRule.replace(
						/^([#|0]+)\.([#|0]+)E\+([#|0]+)/,
						''
					);
				} else {
					if (finishedNumCount >= codeZhengshuNumCount) {
						//进入小数区间,或者分数区间
						if (isFenShuwei === true) {
							if (
								finishedNumCount >
								codeZhengshuNumCount + codeXiaoshuNumCount - 1
							) {
								//进入分母区间了
								const oldReturnHtml = returnHtml;
								if (
									fenmu.toString()[
										finishedNumCount -
											codeZhengshuNumCount -
											codeXiaoshuNumCount
									]
								) {
									returnHtml += fenmu.toString(); //[finishedNumCount - codeZhengshuNumCount - codeXiaoshuNumCount];
								} else if (temp === '0') {
									returnHtml += '0';
								} else if (temp === '?') {
									returnHtml += ' ';
								}
								newFormatRule = newFormatRule.slice(
									returnHtml.length - oldReturnHtml.length
								);
							} else {
								if (
									value.length === 2 &&
									value[1].length > codeXiaoshuNumCount
								) {
									returnHtml += value[1];
									newFormatRule = newFormatRule.slice(codeXiaoshuNumCount);
								} else {
									if (
										value.length === 2 &&
										finishedNumCount - codeZhengshuNumCount >
											codeXiaoshuNumCount - value[1].length - 1
									) {
										returnHtml +=
											value[1][
												value[1].length -
													codeXiaoshuNumCount +
													finishedNumCount -
													codeZhengshuNumCount
											];
									} else if (temp === '0') {
										returnHtml += '0';
									} else if (temp === '?') {
										returnHtml += ' ';
									}
									newFormatRule = newFormatRule.slice(1);
								}
							}
						} else {
							if (
								value.length === 2 &&
								value[1].length > finishedNumCount - codeZhengshuNumCount
							) {
								returnHtml += value[1][finishedNumCount - codeZhengshuNumCount];
							} else if (temp === '0') {
								returnHtml += '0';
							} else if (temp === '?') {
								returnHtml += ' ';
							}
							newFormatRule = newFormatRule.slice(1);
						}
					} else if (
						codeZhengshuNumCount - finishedNumCount <=
						value[0].length
					) {
						// 整数部分   code 3个   value 2个， finishedNumCount应该1就进入
						if (finishedNumCount === 0) {
							// 整数之前的部分(code位少)
							// 整数部分 虽然code的位数不够,则都显示
							for (let i = 0; i < value[0].length - codeZhengshuNumCount; i++) {
								returnHtml += value[0][i];
								if (isQianfenwei && (value[0].length - i) % 3 === 1) {
									returnHtml += ',';
								}
							}
						}
						if (value[0] === '0') {
							if (temp === '0') {
								returnHtml += '0';
							} else if (temp === '?') {
								returnHtml += ' ';
							}
						} else {
							returnHtml +=
								value[0][
									value[0].length + finishedNumCount - codeZhengshuNumCount
								];
						}
						if (!(isFenShuwei && value[0] === '0')) {
							if (
								isQianfenwei &&
								(codeZhengshuNumCount - finishedNumCount) % 3 === 1 &&
								codeZhengshuNumCount - finishedNumCount !== 1
							) {
								returnHtml += ',';
							}
						}
						newFormatRule = newFormatRule.slice(1);
					} else {
						if (temp === '0') {
							returnHtml += '0';
						} else if (temp === '?') {
							returnHtml += ' ';
						}
						if (temp === '0') {
							if (
								isQianfenwei &&
								(codeZhengshuNumCount - finishedNumCount) % 3 === 1 &&
								codeZhengshuNumCount - finishedNumCount !== 1
							) {
								returnHtml += ',';
							}
						}
						newFormatRule = newFormatRule.slice(1);
					}
					finishedNumCount++;
				}
			} else if (temp === '@') {
				returnHtml += oldValue;
				newFormatRule = newFormatRule.slice(1);
			} else if (temp === '"') {
				const findStr: any = newFormatRule.match(/^"([^"]*)"/);
				returnHtml += findStr[1];
				newFormatRule = newFormatRule.replace(/^"([^"]*)"/, '');
			} else if (temp === '\\' || temp === '!') {
				returnHtml += newFormatRule[1];
				newFormatRule = newFormatRule.slice(2);
			} else if (temp === ',') {
				newFormatRule = newFormatRule.slice(1);
			} else if (temp === '.') {
				//真实数字精度大于格式小数精度,则进行四舍五入
				if (
					finishedNumCount === codeZhengshuNumCount &&
					value.length === 2 &&
					value[1].length > codeXiaoshuNumCount
				) {
					// 前面添加一个1，后面再删除，是为了防止第一位是0，造成的省略
					let fixed = Math.round(
						parseFloat(
							'1' +
								value[1].slice(0, codeXiaoshuNumCount) +
								'.' +
								value[1].slice(codeXiaoshuNumCount)
						)
					).toString();
					//	变为2有进位
					if (fixed.slice(0, 1) === '2') {
						returnHtml = (Number(value[0]) + 1).toString();
					}
					fixed = fixed.slice(1);
					value[1] = parseFloat('0.' + fixed)
						.toFixed(codeXiaoshuNumCount)
						.split('.')[1]
						.replace(/0+$/, '');
					if (value[1].match(/^0+$/) !== null) {
						value = [value[0]];
					}
				}
				if (allEffectivePlaceholder) {
					// 如果小数点后面都是#，而且遇到了整数，则判断小数点是无效的
					// ##.## 遇到【3】，最终显示【 3 】，而不是【 3. 】
					if (value[1]) {
						returnHtml += temp;
					}
				} else {
					returnHtml += temp;
				}
				newFormatRule = newFormatRule.slice(1);
			} else {
				returnHtml += temp;
				newFormatRule = newFormatRule.slice(1);
			}
		}
		returnHtml = this.formatDate(returnHtml, oldValue, formatRule);
		returnValue[2] = returnHtml;
		// 如果只配置了规则或者颜色，等于默认填充了值
		if ((usedCalc || styleColor) && returnHtml === '') {
			returnValue[2] = oldValue;
		}
		if (styleColor !== '') {
			returnValue[3] = styleColor;
		}
		return returnValue;
	}

	/**
	 * @description 处理格式化规则
	 * 根据格式化规则对 正 负 零 区分显示规则处理
	 * @private
	 * @param {*} value 值
	 * @param {string} formatRule 格式化规则
	 * @return {*}  {string}
	 * @memberof FormatController
	 */
	private handleFormatRules(value: any, formatRules: string[]): string {
		let matchFunc = [
			// 正数
			function (value: any) {
				return (
					parseFloat(value).toString() === value.toString() &&
					parseFloat(value) > 0
				);
			},
			// 负数
			function (value: any) {
				return (
					parseFloat(value).toString() === value.toString() &&
					parseFloat(value) < 0
				);
			},
			// 0
			function (value: any) {
				return value === 0 || value === '0' || value === '-0';
			},
			// 文本
			function () {
				return true;
			},
		];
		// 是否匹配
		const isMatch = function (value: any, temp1: any, temp2: any) {
			if (temp1 === '=') {
				temp1 = '==';
			}
			switch (temp1) {
				case '>':
					return parseFloat(value) > parseFloat(temp2);
				case '<':
					return parseFloat(value) < parseFloat(temp2);
				case '>=':
					return parseFloat(value) >= parseFloat(temp2);
				case '<=':
					return parseFloat(value) <= parseFloat(temp2);
				case '<>':
					return parseFloat(value) !== parseFloat(temp2);
				case '==':
					return parseFloat(value) === parseFloat(temp2);
			}
		};

		if (
			formatRules.length < 4 &&
			formatRules[0].match(/^\[(>|<|=|>=|<=|<>)\d+\]/)
		) {
			// 使用了条件表达式
			matchFunc = [
				function (value: any) {
					const temp: any = formatRules[0].match(/^\[(>|<|=|>=|<=|<>)(\d+)\]/);
					return isMatch(value, temp[1], temp[2]) || true;
				},
				function (value: any) {
					const temp: any = formatRules[1].match(/^\[(>|<|=|>=|<=|<>)(\d+)\]/);
					return isMatch(value, temp[1], temp[2]) || true;
				},
				function () {
					return true;
				},
			];
		}
		if (formatRules.length === 3) {
			formatRules[3] = '@';
		} else if (formatRules.length === 2) {
			formatRules[3] = '@';
			formatRules[2] = formatRules[0];
		} else if (formatRules.length === 1) {
			if (formatRules[0].includes('@')) {
				formatRules[3] = formatRules[0];
			} else {
				formatRules[3] = '@';
			}
			formatRules[2] = formatRules[0];
			formatRules[1] = '-' + formatRules[0];
		}
		let newFormatRule = '';
		for (const i in matchFunc) {
			if (matchFunc[i](value)) {
				newFormatRule = formatRules[i];
				break;
			}
		}
		return newFormatRule;
	}

	/**
	 * @description 解析数字
	 * @param {number} num 值
	 * @param {number} placeholder 占位数
	 * @return {*}
	 * @memberof FormatController
	 */
	private parseNumber(num: number, placeholder: number) {
		const numList = num.toString().split('.');
		num = parseFloat('0.' + numList[1]);
		let nearHalf = [0, 1];
		for (let fenmu = 1; fenmu < Math.pow(10, placeholder); fenmu++) {
			let begin = 1;
			let end = fenmu - 1;
			let half = 0;
			let tag = true;
			while (tag) {
				half = begin + parseInt(((end - begin) / 2).toString());
				if (half / fenmu === num) {
					begin = half;
					end = half;
					tag = false;
				}
				if (half / fenmu > num) {
					end = half;
				} else {
					begin = half;
				}
				if (end - begin <= 1) {
					tag = false;
				}
			}
			if (half / fenmu === num) {
				nearHalf = [half, fenmu];
				tag = false;
			}
			if (begin / fenmu === num) {
				nearHalf = [begin, fenmu];
				tag = false;
			}
			if (end / fenmu === num) {
				nearHalf = [end, fenmu];
				tag = false;
			}
			if (
				placeholder === 1 ||
				(placeholder === 2 && fenmu >= 10) ||
				(placeholder === 3 && fenmu >= 100)
			) {
				if (
					Math.abs(end / fenmu - num) <
					Math.abs(nearHalf[0] / nearHalf[1] - num)
				) {
					nearHalf = [end, fenmu];
				}
			}
		}
		nearHalf[0] =
			parseInt(nearHalf[0].toString()) + parseFloat(numList[0]) * nearHalf[1];
		return nearHalf;
	}

	/**
	 * @description Html
	 * @param {*} [value0, value1, value2, style]
	 * @return {*}
	 * @memberof FormatController
	 */
	private encodeHtml([value0, value1, value2, style]: any) {
		if (value1) {
			const fillValue = value1.repeat(1000);
			return (
				'<span class="vue-format-single' +
				(style ? ' vue-format-single-color-' + style : '') +
				'">' +
				(value0 ? '<span>' + value0 + '</span>' : '') +
				('<span class="vue-format-single-fill">' + fillValue + '</span>') +
				(value2 ? '<span>' + value2 + '</span>' : '') +
				'</span>'
			);
		} else {
			if (style) {
				return (
					'<span class="vue-format-single' +
					(' vue-format-single-color-' + style) +
					'">' +
					[value0, value1, value2].join('') +
					'</span>'
				);
			} else {
				return [value0, value1, value2].join('');
			}
		}
	}
}

// 导出服务
export const format: FormatController = FormatController.getInstance();
